package com.cwj.os.coursedesgin.bashor;

import com.sun.source.tree.Tree;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;


//文件树
public class FileTree {
//    家目录的父目录名
    public final static String HOME_FILE = "home";
//    顶层目录名
    public final static String ROOT_FILE = "$";
//    保护码隐藏文件名
    public final static String codeFile = ".proCode";
//    文件树的根节点（其指向文件系统根目录）
    private final TreeNode root;
//    当前目录所在文件树的节点
    private TreeNode nowNode;
//    但是节点到根目录的集合
    private List<String> path = new ArrayList<>();
//    基本路径，用户后面的动态变化
    private final static String BASE_PATH = "";
//    这个系统的用户信息隐藏文件
    private final static String Authentication_FILE = ".user";

//    初始化函数
    void init() {
        this.nowNode = this.root;
        path.clear();
        path.add(this.root.getDirName());
    }

    public static String getAuthentication_FILE() {
        return Authentication_FILE;
    }

//    获取用户信息表的全路径
    public String getVerityFileFullPath(){
        return Path.of(BASE_PATH,this.path.get(0),FileTree.Authentication_FILE).toString();
    }
//添加用户
    public boolean addUser(String username, String pawd){
        try(BufferedWriter out = new BufferedWriter(new FileWriter(this.getVerityFileFullPath(),true))){
            out.write(username+" "+pawd);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

//  验证用户身份
    public boolean verifyUser(String username,String pawd,boolean isVerifyUser){

        this.initDir(this.getVerityFileFullPath(),false);

        try(BufferedReader in = new BufferedReader(new FileReader(this.getVerityFileFullPath()))) {

            String line;

            while((line = in.readLine())!=null){
                final String[] s = line.split(" ");
                if(s.length<2)  continue;
                if(s[0].equals(username)){

                    if(!isVerifyUser)  return true;
                    else if(s[1].equals(pawd))   return true;
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

//    获取家目录全路径
    public String getFullHome(){
        return Path.of(FileTree.BASE_PATH,FileTree.BASE_PATH,FileTree.HOME_FILE).toString();
    }
    //    获取家目录全路径
    public List<String> getFullHomePathList(){
        List<String> list = new ArrayList<>();
        list.add(FileTree.BASE_PATH);
        list.add(FileTree.ROOT_FILE);
        list.add(FileTree.HOME_FILE);
        return list;
    }
    public List<String> getFullVRootPathList(){
        List<String> list = new ArrayList<>();
        list.add(FileTree.ROOT_FILE);
        return list;
    }
//  将当前节点定位到家目录的父目录中
    public boolean replaceNodeToHome(){
        this.nowNode = this.root;
//        this.path.clear();
        final List<String> list = this.getFullHomePathList();
        int ind =0;
        for (int i = list.size()-1; i >=0 ; i--) {
            if(list.get(i).equals(FileTree.HOME_FILE))  {ind = i;}
        }
        this.path = this.getFullVRootPathList();
        return replaceNowNode(list.subList(ind,list.size()));
    }
//  定位到某个用户的家目录中
    public boolean replacePersonHomeNode(String name){
        return this.replaceNodeToHome() && this.cdDir(name);
    }

//    定位到给定路径的目录节点中
    public boolean replaceNowNode(List<String> list){
        for (String s : list) {
            if (!this.cdDir(s)) return false;
        }
        return true;
    }
//  善后工作
    public void endDo() {
        this.saveProCode();
    }
//  保存保护码隐藏文件（全系统的）
    private void saveProCode() {
        this.saveProCodeDfs(this.root);
    }
//  递归保存隐藏文件
    private void saveProCodeDfs(TreeNode tn) {
        tn.saveProCodeList();
        final HashMap<String, TreeNode> subDir = tn.getSubDir();
        subDir.forEach((a, k) -> {
            this.cdDir(a);
            this.saveProCodeDfs(k);
            this.cdPre();
        });
    }
//  构造函数
    public FileTree() {
        root = new TreeNode("$", null, this);
        this.initDir(Path.of(FileTree.BASE_PATH, "$").toString(), true);
        init();
        this.nowNode.loadNode();
    }
//  初始化目录
    public boolean initDir(String p, boolean isDir) {
        File rootDir = new File(p);
        if (isDir) {
            if (!rootDir.exists() || !(rootDir.isDirectory())) {
                return rootDir.mkdirs();
            }
        } else {
            if (!rootDir.exists() || rootDir.isDirectory()) {
                try {
                    return rootDir.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return true;
    }

    public FileTree(TreeNode root) {
        this.root = root;
        init();
    }

    public void reset() {
        init();
    }

    public void showSub(Boolean isFull) {
        System.out.println("nowPath:" + this.getPath());
        if (isFull) {
            System.out.println("文件/目录名\t|\t地址\t|\t保护码\t|\t文件长度");
            this.nowNode.showSub(true);
        } else this.nowNode.showSub(false);
    }
//  添加目录
    public Boolean addDir(String name, Boolean isReplace) {
        if (this.nowNode.getSubDir().containsKey(name) && !isReplace) return false;
        this.nowNode.getSubDir().put(name, new TreeNode(name, this.nowNode, this));
        this.initFullInitDir(name);
        return true;
    }
//  移除文件
    public Boolean removeFile(String name) {
        File f = new File(Path.of(this.getFaPath(), name).toString());
        if (!f.exists() || f.isDirectory()) return true;
        f.deleteOnExit();
        this.nowNode.getFiles().remove(name);
        return true;
    }
//  移除目录
    public boolean removeDir(String name, boolean isDelFull) {
        File f = new File(Path.of(this.getFaPath(), name).toString());

        if (this.nowNode.getSubDir().remove(name) == null) return true;

        if (!f.exists() || !f.isDirectory()) return true;

        boolean flag = false;

        if (isDelFull) {
            try {
                Files.walkFileTree(f.toPath(), new SimpleFileVisitor<Path>() {
                    @Override
                    public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException {
                        Files.delete(file); // 有效，因为它始终是一个文件
                        return FileVisitResult.CONTINUE;
                    }

                    @Override
                    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                        Files.delete(dir); //这将起作用，因为目录中的文件已被删除
                        return FileVisitResult.CONTINUE;
                    }
                });
                flag = true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            flag = f.delete();
        }
        return flag;
    }
//  获取父目录路径
    public String getFaPath() {
        String p = FileTree.BASE_PATH;
        for (String s : this.path) {
            p = Path.of(p, s).toString();
        }
        return p;
    }

    public Boolean initFullInitDir(String name) {
        return this.initDir(Path.of(this.getFaPath(), name).toString(), true);
    }

    public Boolean initFullInitFile(String name) {
        return this.initDir(Path.of(this.getFaPath(), name).toString(), false);
    }

//    添加文件
    public Boolean addFile(String name, ProCode code, boolean isReplace) {
        this.initFullInitFile(name);
        this.nowNode.getProCodeList().put(name, code);
        return this.nowNode.getFiles().insert(new MyFile(name, this.nowNode), isReplace);
    }

//  进入某个子目录
    public Boolean cdDir(String str) {
        final TreeNode treeNode = this.nowNode.getSubDir().get(str);
        if (treeNode != null) {
            this.nowNode = treeNode;
            this.path.add(str);
            this.nowNode.loadNode();
            return true;
        }
        return false;
    }
//  回到上一层目录
    public Boolean cdPre() {
        if (this.nowNode.getFa() == null) return false;
        this.nowNode = this.nowNode.getFa();
        this.path.remove(this.path.size() - 1);
        this.nowNode.loadNode();
        return true;
    }
//  获取保护码文件
    public ProCode getFileReadCode(String name) {
        TireNode<MyFile> tm = this.nowNode.getFiles().find(name);
        if (tm == null) return null;
        MyFile fm = tm.getFile();
        if (fm == null) return null;
        return this.nowNode.getProCodeList().get(name);
    }
//  展示子文件内容
    public boolean showFileContext(String name) {
        TireNode<MyFile> tn = this.nowNode.getFiles().find(name);
        if (tn == null) return false;
        MyFile mf = tn.getFile();
        if (mf == null) return false;
        try (BufferedReader in = new BufferedReader(new FileReader(Path.of(this.getFaPath(), mf.getName()).toString()))) {
            String str;
            while ((str = in.readLine()) != null) System.out.println(str);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }


    public static String getCodeFile() {
        return codeFile;
    }

    public TreeNode getRoot() {
        return root;
    }

    public TreeNode getNowNode() {
        return nowNode;
    }

    public String getPath() {
        String p="";
        final int size = this.path.size();

        for (int i = 0; i < size; i++) {
            if(size>=3&&i<2)    continue;
            p = Path.of(p,this.path.get(i)).toString();
        }
        return p;
    }

    public static String getHomeFile() {
        return HOME_FILE;
    }



    public static String getRootFile() {
        return ROOT_FILE;
    }

    public static String getBasePath() {
        return BASE_PATH;
    }
}

//  文件目录树的结点

class TreeNode {


    private String dirName;//目录名
    private boolean isLoad;//目录是否被加载
    private TreeNode fa;//其父目录是哪一个
    private FileTree tree;//所属树

    //    private HashMap<String, MyFile> files;//本目录下的文件
    private FileTrie files = new FileTrie(this);//本目录下的文件 ,用字典树来维护
    private HashMap<String, TreeNode> subDir = new HashMap<>();//子目录
    private final HashMap<String, ProCode> proCodeList = new HashMap<>();//保护码列表

    //    private FileTrie
    public TreeNode(String dirName, TreeNode fa, FileTree tree) {
        this.dirName = dirName;
        this.fa = fa;
        this.tree = tree;
        this.isLoad = false;
    }
//  懒加载机制。动态加载保护码文件
    private void loadProCode() {
        File codeFile = new File(Path.of(this.tree.getFaPath(), FileTree.getCodeFile()).toString());
        if (!codeFile.exists()) {
            try {
                codeFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        String line;
        try (BufferedReader in = new BufferedReader(new FileReader(codeFile))) {
            while ((line = in.readLine()) != null) {
                final String[] strs = line.split("\t");
//                System.out.println("line="+ Arrays.toString(Arrays.stream(strs).toArray()));
                if(strs.length<2)   continue;
                this.proCodeList.put(strs[0], new ProCode(strs[1], strs.length < 3 ? "": strs[2]));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //  懒加载机制。动态加载磁盘中的目录信息，而不是一开始就全部加载
    public boolean loadNode() {

        if (this.isLoad) return true;

        this.loadProCode();

        String faPath = this.tree.getFaPath();
        File f = new File(faPath);
        final File[] fs = f.listFiles();
        assert fs != null;
        for (File file : fs) {
            String fsn = file.getName();
            if (file.isDirectory()) {
                this.subDir.put(fsn, new TreeNode(fsn, this, this.tree));
            } else {
                this.files.insert(new MyFile(fsn, this.tree.getNowNode()), true);
            }
        }
        this.isLoad = true;
        return true;
    }

    public String getFullPath() {
        return Path.of(this.tree.getFaPath(), this.dirName).toString();
    }

    public FileTrie getFiles() {
        return files;
    }

    public void setFiles(FileTrie files) {
        this.files = files;
    }
//  保存被结点的保护码
    public void saveProCodeList() {
        try (BufferedWriter out = new BufferedWriter(new FileWriter(Path.of(this.tree.getFaPath(), FileTree.getCodeFile()).toString()))) {
            this.getProCodeList().forEach((k, v) -> {
                try {
                    System.out.println(this.getFullPath()+"---"+k + "\t" + v.getReadCode()+"\t"+v.getWriteCode());
                    out.write(k + "\t" + v.getReadCode()+"\t"+v.getWriteCode());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    public void showSub(Boolean isFull) {
        if (isFull) {
            this.subDir.forEach((a, k) -> {
                System.out.println(k);
            });
            this.files.getFullFile().forEach(System.out::println);
        } else {
            StringBuilder sb = new StringBuilder();
            this.subDir.forEach((k, v) -> {
                sb.append("./").append(v.getDirName()).append('\t');
            });
            this.files.getFullFile().forEach((a) -> {
                if (a.getName().charAt(0) != '.') {
                    sb.append(a.getName()).append("\t");
                }
            });
            System.out.println(sb.toString());
        }
    }

    public HashMap<String, ProCode> getProCodeList() {
        return proCodeList;
    }

    public String getDirName() {
        return dirName;
    }

    public void setDirName(String dirName) {
        this.dirName = dirName;
    }

    public boolean isLoad() {
        return isLoad;
    }

    public void setLoad(boolean load) {
        isLoad = load;
    }

    public TreeNode getFa() {
        return fa;
    }

    public void setFa(TreeNode fa) {
        this.fa = fa;
    }

    public FileTree getTree() {
        return tree;
    }

    public void setTree(FileTree tree) {
        this.tree = tree;
    }

    public HashMap<String, TreeNode> getSubDir() {
        return subDir;
    }

    public void setSubDir(HashMap<String, TreeNode> subDir) {
        this.subDir = subDir;
    }

    @Override
    public String toString() {

        return dirName + "\t|\t" + this.getFullPath();
    }

    public static void main(String[] args) {
        System.out.println(false | true);
        ArrayList<String> str = new ArrayList<>();
        str.add("1234");
        str.add("qwer");
        System.out.println(Path.of("1234", "qwer"));
    }

}
//  保护码数据结构
class ProCode {
    String readCode;//读权限保护码
    String writeCode;//写权限保护码

    public ProCode(String readCode, String writeCode) {
        this.readCode = readCode;
        this.writeCode = writeCode;
    }

    public String getReadCode() {
        return readCode;
    }

    public void setReadCode(String readCode) {
        this.readCode = readCode;
    }

    public String getWriteCode() {
        return writeCode;
    }

    public void setWriteCode(String writeCode) {
        this.writeCode = writeCode;
    }



    @Override
    public String toString() {
        return "readCode='" + readCode + '\'' +
                ", writeCode='" + writeCode + '\'';
    }
}


